using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
using SimpleJSON;
using UnityEngine;
using WebSocketSharp.Net;

namespace Gj.aliyun
{
    public class Aliyun
    {
        public static Aliyun Instance;
        public static AliyunSettings settings;
        static Aliyun()
        {
            settings = LoadSettings();
            Instance = new Aliyun();
        }

        private static AliyunSettings LoadSettings()
        {
            AliyunSettings loadedSettings = (AliyunSettings)Resources.Load("AliyunSettings", typeof(AliyunSettings));
            if (loadedSettings == null)
            {
                loadedSettings = ScriptableObject.CreateInstance<AliyunSettings>();
            }
            return loadedSettings;
        }
        public class PolicyConfig
        {
            public string expiration { get; set; }
            public List<List<object>> conditions { get; set; }
        }

        public class PolicyToken
        {
            public string Accessid { get; set; }
            public string Policy { get; set; }
            public string Signature { get; set; }
            public string Dir { get; set; }
            public string Host { get; set; }
            public string Expire { get; set; }
        }
        private static string ToUnixTime(DateTime dateTime)
        {
            return ((DateTimeOffset)dateTime).ToUnixTimeSeconds().ToString();
        }
        private static string FormatIso8601Date(DateTime dtime)
        {
            return dtime.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
                CultureInfo.CurrentCulture);
        }

        private static string EncodeBase64(string codeType, string code)
        {
            string encode = "";
            byte[] bytes = Encoding.GetEncoding(codeType).GetBytes(code);
            try
            {
                encode = Convert.ToBase64String(bytes);
            }
            catch
            {
                encode = code;
            }
            return encode;
        }

        private static string ComputeSignaturePolicy(string key, string data)
        {
            using (var algorithm = new HMACSHA1(Encoding.UTF8.GetBytes(key)))
            {
                return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(data)));
            }
        }
        public static PolicyToken GetPolicyToken(string dir)
        {
            var expireDateTime = DateTime.Now.AddSeconds(3600);
            var config = new PolicyConfig
            {
                expiration = FormatIso8601Date(expireDateTime),
                conditions = new List<List<object>>()
            };
            config.conditions.Add(new List<object>
            {
                "content-length-range", 0, 1048576000
            });
            var policy = JsonConvert.SerializeObject(config);
            var policyBase64 = EncodeBase64("utf-8", policy);
            var signature = ComputeSignaturePolicy(settings.AccessKeySecret, policyBase64);
            var policyToken = new PolicyToken
            {
                Accessid = settings.AccessKeyId,
                Host = settings.OSSHost,
                Policy = policyBase64,
                Signature = signature,
                Expire = ToUnixTime(expireDateTime),
                Dir = dir + "/",
            };
            return policyToken;
        }

        public static string Authorization(PolicyToken token)
        {
            var data = $"";
            //var sign = ComputeSignature256(settings.AccessKeySecret, data);
            return $"OSS4-HMAC-SHA256 Credential={token.Accessid}/{DateTools.GetNow().ToString("yyyyMMdd")}/{settings.Region}/oss/aliyun_v4_request,Signature={token.Signature}";
        }
        static string GenerateSignedUrl(string endpoint, string accessKeyId, string accessKeySecret, string bucketName, string objectName)
        {
            // 过期时间（当前时间 + 1 小时）
            DateTime expiration = DateTime.UtcNow.AddHours(1);
            long expirationTimestamp = (long)(expiration - new DateTime(1970, 1, 1)).TotalSeconds;

            // 构造待签名的字符串
            string stringToSign = $"GET\n\n\n{expirationTimestamp}\n/{bucketName}/{objectName}";

            // 使用 HMAC-SHA1 计算签名
            using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(accessKeySecret)))
            {
                byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
                string signature = Convert.ToBase64String(hashBytes);

                // 构造带签名的 URL
                string signedUrl = $"{endpoint}/{objectName}?OSSAccessKeyId={accessKeyId}&Expires={expirationTimestamp}&Signature={Uri.EscapeDataString(signature)}";
                return signedUrl;
            }
        }

        public static void SaveRecord(JSONNode content, string uuid, Action<bool, string> CB)
        {
            var fileBytes = System.Text.Encoding.UTF8.GetBytes(content.ToString());
            // Debug.LogError($"saveRecord: {content} {fileBytes.Length}");
            UploadOSS(uuid, "", settings.RecordDir, fileBytes, "text/plain", CB);
        }

        public static void LoadRecord(string uuid, Action<bool, JSONNode> CB)
        {
            LogTools.Log("LoadRecord: {0}", uuid);
            DownloadOSS(uuid, "", settings.RecordDir, (ok, fileBytes) =>
            {
                if (!ok) CB(ok, null);
                else
                {
                    var content = System.Text.Encoding.UTF8.GetString(fileBytes);
                    // Debug.LogError($"LoadRecord: {content} {fileBytes.Length}");
                    CB(ok, JSONNode.Parse(content));
                }
            });
        }

        public static void DownloadOSS(string filename, string suffix, string dir, Action<bool, byte[]> CB)
        {
            var token = GetPolicyToken(dir);
            string key = dir + "/" + HttpUtility.UrlEncode(filename) + suffix;
            Dictionary<string, string> headers = new Dictionary<string, string>();
            //var authorization = Authorization(token);
            //Debug.Log($"Authorization {authorization}");
            //headers.Add("Authorization", authorization); 
            var url = settings.OSSHost + "/" + key;
            // url = GenerateSignedUrl(settings.OSSHost, settings.AccessKeyId, settings.AccessKeySecret, "youlong-game",key);
            Game.single.StartCoroutine(HttpTools.GetInstance().Get(url, new Dictionary<string, string>(), headers, (success, www) =>
            {
                if (success)
                {
                    CB(true, www.bytes);
                }
                else
                {
                    CB(false, System.Text.Encoding.UTF8.GetBytes(www.error));
                }

            }));
        }

        public static void UploadOSS(string filename, string suffix, string dir, byte[] fileBytes, string mime, Action<bool, string> CB)
        {
            var token = GetPolicyToken(dir);
            string key = dir + "/" + filename + suffix;
            string policy = token.Policy;
            string signature = token.Signature;
            Dictionary<string, string> headers = new Dictionary<string, string>();
            var boundary = "--" + StringTools.GenerateStr(32) + "--";
            headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
            var requestBody = "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"key\"\r\n"
                              + "\r\n" + key + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"OSSAccessKeyId\"\r\n"
                              + "\r\n" + settings.AccessKeyId + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"policy\"\r\n"
                              + "\r\n" + policy + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"Signature\"\r\n"
                              + "\r\n" + signature + "\r\n"
                              + "--" + boundary + "\r\n"
                              + "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"\r\n"
                              + "Content-Type: " + mime + "\r\n"
                              + "\r\n";
            var requestBodyByte = System.Text.Encoding.UTF8.GetBytes(requestBody);

            var lastBody = "\r\n--" + boundary + "--\r\n";
            var requestlastBodyByte = System.Text.Encoding.UTF8.GetBytes(lastBody);

            //WWWForm wf = new WWWForm();
            //headers.Add("Content-Length", requestBody.Length.ToString());
            //wf.AddField("OSSAccessKeyId", OSSAccessKeyId);
            //wf.AddField("policy", policy);
            //wf.AddField("Signature", signature);
            //wf.AddField("key", key);
            //wf.AddField("success_action_status", result["success_action_status"].AsInt);
            //wf.AddBinaryData("file", File.ReadAllBytes(filepath), file.Name, mime);
            var by = new byte[requestBodyByte.Length + fileBytes.Length + requestlastBodyByte.Length];

            Array.Copy(requestBodyByte, 0, by, 0, requestBodyByte.Length);
            Array.Copy(fileBytes, 0, by, requestBodyByte.Length, fileBytes.Length);
            Array.Copy(requestlastBodyByte, 0, by, requestBodyByte.Length + fileBytes.Length, requestlastBodyByte.Length);
            Game.single.StartCoroutine(HttpTools.GetInstance().Post(settings.OSSHost, by, headers, (success, www) =>
            {
                if (success)
                {
                    CB(true, "success");
                }
                else
                {
                    CB(false, www.error);
                }

            }));
        }
    }
}